package com.xiaoyu.im.provider;

import android.app.Activity;
import android.text.TextUtils;

import androidx.appcompat.app.AlertDialog;

import com.netease.nimlib.sdk.ResponseCode;
import com.xiaoyu.base.entity.ConversationEntity;
import com.xiaoyu.im.datamodel.common.IMConstant;
import com.xiaoyu.im.IMLog;
import com.xiaoyu.im.datamodel.AppMessage;
import com.xiaoyu.im.datamodel.AppMessageReceipt;
import com.xiaoyu.im.datamodel.IMOnlineStatus;
import com.xiaoyu.im.datamodel.exception.IMException;
import com.xiaoyu.im.request.MessageRequest;
import com.xiaoyu.base.DBCall;
import com.xiaoyu.base.app.App;
import com.xiaoyu.base.app.GlobalUI;
import com.xiaoyu.base.data.DocData;
import com.xiaoyu.base.data.UserData;
import com.xiaoyu.base.entity.MessageEntity;
import com.xiaoyu.base.serverpush.data.ServerPushEventData;
import com.xiaoyu.base.utils.ListUtil;
import com.xiaoyu.im.client.IMClient;

import java.util.List;

import in.srain.cube.concurrent.AppThreads;
import in.srain.cube.request.JsonData;
import in.srain.cube.util.CLog;
import in.srain.cube.util.StringUtil;
import in.srain.cube.util.general.GeneralUtil;
import in.srain.cube.util.internal.AppCallback;
import in.srain.cube.util.internal.AppConsumer;
import in.srain.cube.util.internal.AppConsumer2;
import in.srain.cube.util.time.TimeData;

public abstract class AbstractMessageDataProvider {

    private static final long RECALL_TIME_INTERVAL = 2 * 60 * 1000;

    protected AppConsumer2<AppMessage, AppMessage> mOnMessageSentFunction;
    private AppCallback<AppMessage> mOnMessageRecallCallback;

    public void setOnMessageRecallCallback(AppCallback<AppMessage> onMessageRecallCallback) {
        this.mOnMessageRecallCallback = onMessageRecallCallback;
    }

    public static AppMessage loadMessage(final String mid) {
        MessageEntity entity = DBCall.call(database -> database.messageDao().getMessage(mid));
        return entity == null ? null : AppMessage.fromEntity(entity);
    }

    public SearchMessageResultWrapper loadMessagesWithWrapper(String chatId, long lastTime, String lastId) {
        List<MessageEntity> olderEntities = DBCall.listCall(database -> database.messageDao().getMessagesByMessageIdAboveNum(chatId, lastTime, lastId, 20));
        List<MessageEntity> newerEntities = DBCall.listCall(database -> database.messageDao().getMessagesByMessageId(chatId, lastTime, lastId));
        olderEntities.addAll(newerEntities);

        List<AppMessage> messages = ListUtil.convertToList(olderEntities, AppMessage::fromEntity);
        return new SearchMessageResultWrapper(messages, newerEntities.size() - 1);
    }

    public List<AppMessage> loadMessages(String chatId, long lastDisplayTime, String lastId, int num) {
        List<MessageEntity> entities = DBCall.listCall(database -> database.messageDao().getMessagesByChatId(chatId, lastDisplayTime, lastId, num));
        return ListUtil.convertToList(entities, AppMessage::fromEntity);
    }

    public List<AppMessage> loadMessages(String chatId, int num) {
        List<MessageEntity> entities = DBCall.listCall(database -> database.messageDao().getMessagesByChatId(chatId, num));
        return ListUtil.convertToList(entities, AppMessage::fromEntity);
    }

    public void onMessageSent(MessageRequest request) {
        CLog.d(IMConstant.LOG_TAG, "onMessageSent: %s", request);

        final AppMessage successMessage = request.getSuccessMessage();
        if (successMessage == null) {
            throw new IllegalStateException("call onMessageSent but not found success AppMessage from MessageRequest");
        }
        final AppMessage pendingMessage = request.getPendingMessage();

        MessageEntity pendingEntity = pendingMessage.toEntity();
        DBCall.run(database -> database.messageDao().delete(pendingEntity));

        MessageReceiptData.getInstance().updateOnMessageSent(pendingMessage, successMessage, mOnMessageSentFunction);
        ConversationManager.getInstance().onMessageOut(request.getChatId());
    }

    public void onMessageFail(MessageRequest request) {
        CLog.d(IMConstant.LOG_TAG, "onMessageFail: %s", request);
        AppMessage pendingMessage = request.getPendingMessage();

        MessageEntity entity = pendingMessage.toEntity();
        DBCall.run(database -> database.messageDao().upsert(entity));

        processMessageFailureForDialog(request);
    }

    public void onMessageReceived(AppMessage message) {
        CLog.d(IMConstant.LOG_TAG, "onMessageReceived %s", message);
        final String chatId = message.chatToken.getChatId();
        ConversationEntity conversation = ConversationManager.getInstance().fetchConversation(chatId);
        if (conversation == null) {
            processEventLogForReceiveMessageError(message);
            CLog.e(IMConstant.LOG_TAG, "onMessageReceived, waiting ensure conversation for id:" + chatId);
            return;
        }
        processEventLogForReceiveMessage(message, conversation.getType(), conversation.getName());
        ConversationManager.getInstance().onMessageIn(chatId);
    }

    public void onBroadcastMessage(String content) {
        JsonData jsonData = JsonData.create(content);
        String type = jsonData.optString("type");
        if (TextUtils.equals(type, "server_push")) {
            JsonData data = jsonData.optJson("data");
            ServerPushEventData.getInstance().processEventList(data.optJson("event_list"));
        }
    }

    protected void onMessageRecall(AppMessage message) {
        CLog.d(IMConstant.LOG_TAG, "onMessageRecall %s", message);
    }

    public void onMessageReceipt(List<AppMessageReceipt> appMessageReceipts, AppConsumer2<String, Long> updateConsumer) {
        MessageReceiptData.getInstance().updateOnMessageReceipt(appMessageReceipts, updateConsumer);
    }

    private void recallMessage(AppMessage message) {
        long interval = TimeData.getInstance().getServerTime().getTime() - message.time;
        if (interval > RECALL_TIME_INTERVAL) {
            GlobalUI.getInstance().showToast(DocData.getInstance().getDoc("recall_error.toast"));
            return;
        }
        MessageEntity entity = DBCall.call(database -> database.messageDao().getMessage(message.id));
        if (entity == null) {
            GlobalUI.getInstance().showToast("撤回失败");
            return;
        }
        AppMessage appMessage = AppMessage.fromEntity(entity);
        IMClient.getInstance().recallMessage(appMessage, (success, result, exception) -> {
            if (success) {
                mOnMessageRecallCallback.onSuccess(appMessage);
                onMessageRecallAsync(appMessage);
            } else {
                mOnMessageRecallCallback.onError(exception);
            }
        });
    }

    protected void deleteMessage(AppMessage message) {
        // 标记不可见而不是直接删除，不然检测到消息有断层会重新再拉回来
        DBCall.run(database -> database.messageDao().updateInvisible(message.id));
        ConversationManager.getInstance().onMessageRemove(message);
    }

    private void resendMessage(AppMessage message) {
        MessageEntity entity = DBCall.call(database -> database.messageDao().getMessage(message.id));
        if (entity == null) {
            CLog.e(IMConstant.LOG_TAG, "resendMessage entity not found by id: " + message.id);
            return;
        }
        MessageRequest request = MessageRequest.fromEntity(entity);
        request.reSend();
    }

    public void preparePendingMessage(MessageRequest request) {
        CLog.d(IMConstant.LOG_TAG, "preparePendingMessage: %s", request);
        final AppMessage pendingMessage = request.getPendingMessage();

        final MessageEntity entity = pendingMessage.toEntity();
        DBCall.run(database -> database.messageDao().upsert(entity));
        ConversationManager.getInstance().onMessageOut(request.getChatId());
    }

    public void searchTextMessage(String uid, String chatId, String keyword, int limit, AppConsumer<List<AppMessage>> consumer) {
        GeneralUtil.runFromBackgroundToMain(() -> searchTextMessage(uid, chatId, keyword, limit), consumer);
    }

    private List<AppMessage> searchTextMessage(String uid, String chatId, String keyword, int limit) {
        List<MessageEntity> messageEntities;

        if (TextUtils.isEmpty(chatId)) {
            messageEntities = DBCall.listCall(uid, database -> database.messageDao().searchTextMessage(keyword, limit));
        } else {
            messageEntities = DBCall.listCall(uid, database -> database.messageDao().searchTextMessageByChatId(chatId, keyword, limit));
        }
        return ListUtil.convertToList(messageEntities, AppMessage::fromEntity);
    }

    public void recallMessageAsync(AppMessage message) {
        AppThreads.runOnIOThread(() -> recallMessage(message));
    }

    public void deleteMessageAsync(AppMessage message) {
        AppThreads.runOnIOThread(() -> deleteMessage(message));
    }

    public void resendMessageAsync(AppMessage message) {
        AppThreads.runOnIOThread(() -> resendMessage(message));
    }

    public void onMessageRecallAsync(AppMessage message) {
        AppThreads.runOnIOThread(() -> onMessageRecall(message));
    }

    public void onMessageNewTipUpdateAsync(AppMessage message) {
        AppThreads.runOnIOThread(() -> onMessageNewTipUpdate(message));
    }

    public abstract void onReceiveAudioMessagePlayCompleteAsync(AppMessage message);

    protected void onMessageNewTipUpdate(AppMessage message) {
        message.readNewTip();

        String uid = UserData.getInstance().getUid();
        DBCall.run(uid, database -> database.messageDao().upsert(message.toEntity()));
    }

    protected void processEventLogForReceiveMessageError(AppMessage message) {
        IMLog.logImErrorForReceiveMessage(message);
    }

    protected void processEventLogForReceiveMessage(AppMessage message, String conversationType, String conversationName) {
        IMLog.logImStreamForReceiveMessage(message, conversationType, conversationName);
    }

    private void processMessageFailureForDialog(MessageRequest request) {
        Throwable throwable = request.getThrowable();
        int errorCode = IMException.getIMExceptionCode(throwable);
        String onlineStatus = IMClient.getInstance().getOnlineStatus();

        if (errorCode == ResponseCode.RES_EXCEPTION && IMOnlineStatus.UNLOGIN.equals(onlineStatus)) {
            AppThreads.runOnMainThread(() -> {
                Activity topActivity = App.getInstance().getTopActivity();
                if (topActivity != null && !topActivity.isDestroyed() && !topActivity.isFinishing()) {
                    new AlertDialog.Builder(topActivity)
                            .setMessage(DocData.getInstance().getDoc("im.send.failure.res_exception.dialog", "发送消息异常，请稍后再试"))
                            .setPositiveButton(DocData.getInstance().getDoc("action.confirm", "确定"), (dialog, which) -> {
                                IMClient.getInstance().login();
                            })
                            .show();
                }
            });
        } else if (errorCode == ResponseCode.RES_ETIMEOUT) {
            AppThreads.runOnMainThread(() -> {
                Activity topActivity = App.getInstance().getTopActivity();
                if (topActivity != null && !topActivity.isDestroyed() && !topActivity.isFinishing()) {
                    new AlertDialog.Builder(topActivity)
                            .setMessage(DocData.getInstance().getDoc("im.send.failure.net_exception.dialog", "网络异常，请稍后再试"))
                            .setPositiveButton(DocData.getInstance().getDoc("action.confirm", "确定"), (dialog, which) -> {
                            })
                            .show();
                }
            });
        }
    }

    public static class SearchMessageResultWrapper {
        public final List<AppMessage> messages;
        public final int messageIndex;

        public SearchMessageResultWrapper(List<AppMessage> messages, int messageIndex) {
            this.messages = messages;
            this.messageIndex = messageIndex;
        }
    }
}
